"""
GDB-side logic: breakpoint handling, event capture, serialization.
"""

import gdb
import time
from typing import Dict, Any

from config_loader import load_config, ConfigError  # adjust import path if needed
from adapters import get_adapter
from serializers import build_serializer

# ---------- Utils ----------

def current_depth(target_frame: str) -> int:
    depth = 0
    frame = gdb.selected_frame()
    while frame:
        if frame.name() == target_frame:
            depth += 1
        frame = frame.older()
    return depth

# ---------- Watch Manager ----------

class WatchManager:
    def __init__(self, config: Dict[str, Any]) -> None:
        self.config = config
        self.watched = config.get("watched", [])
        self.serializers = build_serializer(config)  # simplified: single serializer
        self.adapters_cache = {}

    def get_adapter(self, adapter_name: str):
        if adapter_name not in self.adapters_cache:
            self.adapters_cache[adapter_name] = get_adapter(adapter_name)
        return self.adapters_cache[adapter_name]

    def capture(self, event_name: str) -> None:
        variables = {}

        for item in self.watched:
            name = item["name"]
            adapter_name = item.get("adapter", "primitive")
            try:
                value = gdb.parse_and_eval(name)
                adapter = self.get_adapter(adapter_name)
                payload = adapter.serialize(value)
            except Exception as exc:
                payload = {"__error__": str(exc)}

            variables[name] = payload

        payload = {
            "timestamp": time.time(),
            "event": event_name,
            "frame": gdb.selected_frame().name(),
            "location": str(gdb.find_pc_line(gdb.selected_frame().pc())),
            "depth": current_depth(self.config.get("target_frame", "backtrack")),
            "variables": variables,
        }
        self.serializers.emit(payload)

# ---------- Breakpoint Handlers ----------

class ConfiguredBreakpoint(gdb.Breakpoint):
    def __init__(self, spec: str, event_name: str, manager: WatchManager):
        super().__init__(spec, internal=False)
        self.event_name = event_name
        self.manager = manager

    def stop(self):
        self.manager.capture(self.event_name)
        return False  # continue automatically

# ---------- Entry Point ----------

class AlgVizCommand(gdb.Command):
    """Invoke as: algviz-load /path/to/config.yml"""

    def __init__(self):
        super().__init__("algviz-load", gdb.COMMAND_USER)

    def invoke(self, arg, from_tty):
        try:
            cfg = load_config(arg.strip())
        except ConfigError as exc:
            gdb.write(f"[algviz] config error: {exc}\n", gdb.STDERR)
            return

        manager = WatchManager(cfg)
        for bp in cfg.get("breakpoints", []):
            location = bp["location"]
            event_name = bp.get("event", "event")
            ConfiguredBreakpoint(location, event_name, manager)
            gdb.write(f"[algviz] breakpoint set: {location} -> {event_name}\n")

        entry = cfg.get("entry")
        if entry:
            gdb.execute(f"run", to_string=False)

AlgVizCommand()